package bigbeard.tools.net.server.impl;



import bigbeard.tools.net.server.BaseServerImpl;
import bigbeard.tools.net.api.DataArrivalCallback;
import bigbeard.tools.net.api.StateChangeCallback;
import bigbeard.tools.net.api.server.ClientAcceptCallback;
import bigbeard.tools.net.api.server.IConnector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

/**
 * @author bigbeard on 2022-09-29
 */
public class WebsocketServerImpl extends BaseServerImpl {
    private static final Log log = LogFactory.getLog(MyWebsocket.class);


    private MyWebsocket myWebsocket;

    @Override
    public void start(int port) throws Exception {
        myWebsocket = new MyWebsocket(port);
        myWebsocket.clientAcceptCallback = this.clientAcceptCallback;
        myWebsocket.dataArrivalCallback = this.dataArrivalCallback;
        myWebsocket.stateChangeCallback = this.stateChangeCallback;
        myWebsocket.start();
    }

    @Override
    public void stop() {
        if (null != myWebsocket) {
            try {
                myWebsocket.stop();
            } catch (Exception ignored) {
            }
        }
    }


    private static class MyWebsocket extends WebSocketServer {

        private String locationPoint = "";

        DataArrivalCallback dataArrivalCallback;
        StateChangeCallback stateChangeCallback;
        ClientAcceptCallback clientAcceptCallback;


        private void notifyState(String key, int state, String info) {
            if (null != stateChangeCallback) {
                stateChangeCallback.notifyStateChange(key, state, info);
            }
        }

        private void notifyDataArrival(String key, byte[] data) {
            if (null != dataArrivalCallback) {
                dataArrivalCallback.callback(key, data);
            }
        }

        private void notifyClientAccept(String key, IConnector connector) {
            if (null != clientAcceptCallback) {
                clientAcceptCallback.accept(key, connector);
            }
        }

        public MyWebsocket(int port) {
            this(new InetSocketAddress(port));
        }

        public MyWebsocket(InetSocketAddress address) {
            super(address);
            locationPoint = address.toString();
        }

        @Override
        public void onOpen(WebSocket conn, ClientHandshake handshake) {
            log.info("收到:" + conn.getRemoteSocketAddress().toString() + "," + handshake.getResourceDescriptor() +
                     "的连接请求");
            IConnector connector = new WebsocketConnectorImpl();
            connector.setConnector(conn);
            connector.setCode(handshake.getResourceDescriptor());
            connector.updateLastMsgTime();
            notifyClientAccept(String.valueOf(conn.hashCode()), connector);
        }

        @Override
        public void onClose(WebSocket conn, int code, String reason, boolean remote) {
            String info = "code=" + code + ",reason=" + reason +
                          ",remote=" + remote;
            log.info("收到:" + conn.getRemoteSocketAddress().toString() + ",关闭" + info);
            notifyState(String.valueOf(conn.hashCode()), StateChangeCallback.CLIENT_CLOSE, info);
        }


        @Override
        public void onMessage(WebSocket conn, String message) {
            log.info("收到[" + conn.getRemoteSocketAddress().toString() + "]:" + message);
            notifyDataArrival(String.valueOf(conn.hashCode()), message.getBytes(StandardCharsets.UTF_8));
        }

        @Override
        public void onError(WebSocket conn, Exception ex) {
            if (null != conn) {
                log.info("收到[" + conn + "]异常:" + ex);
                notifyState(String.valueOf(conn.hashCode()), StateChangeCallback.CLIENT_ERROR, ex.getMessage());
                return;
            }
            log.info("未知连接异常:" + ex);
            log.error(ex);
        }

        @Override
        public void onStart() {
            notifyState("", StateChangeCallback.SERVER_OPEN, "服务器在[" + locationPoint + "]启动成功!");
        }
    }
}
